home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / x11 / strategy / xshisen-.001 / xshisen-~ / xshisen-1.35 / score.C < prev    next >
C/C++ Source or Header  |  1996-01-22  |  20KB  |  640 lines

  1. #include <pwd.h>
  2. #include "components.h"
  3. #include "kconv.h"
  4.  
  5. XtResource Score::resources[] = {
  6.     { "scoreTitle",  "ScoreTitle",  XtRString, sizeof(char *),
  7.       0*sizeof(char *), XtRString, "XShisen High Score\n\n" },
  8.     { "scoreFormat", "ScoreFormat", XtRString, sizeof(char *),
  9.       1*sizeof(char *), XtRString, "%2d  %2.2d:%2.2d:%2.2d  %-28s  %s %s\n" },
  10.     { "strPeriod", "StrPeriod", XtRString, sizeof(char *),
  11.       2*sizeof(char *), XtRString, "Last %d days" },
  12.     { "averagePeriod", "AveragePeriod", XtRString, sizeof(char *),
  13.       3*sizeof(char *), XtRString, "14" },
  14.     { "strPlayed", "StrPlayed", XtRString, sizeof(char *),
  15.       4*sizeof(char *), XtRString, "Played" },
  16.     { "strCompleted", "StrCompleted", XtRString, sizeof(char *),
  17.       5*sizeof(char *), XtRString, "Completed" },
  18.     { "strTedumari", "StrTedumari", XtRString, sizeof(char *),
  19.       6*sizeof(char *), XtRString, "Deadlocked" },
  20.     { "strSearched", "StrSearched", XtRString, sizeof(char *),
  21.       7*sizeof(char *), XtRString, "Seach used" },
  22.     { "strGiveUp", "StrGiveUp", XtRString, sizeof(char *),
  23.       8*sizeof(char *), XtRString, "Gaven up" },
  24.     { "strTotal", "StrTotal", XtRString, sizeof(char *),
  25.       9*sizeof(char *), XtRString, "Total" },
  26.     { "strAverage", "StrAverage", XtRString, sizeof(char *),
  27.       10*sizeof(char *), XtRString, "Whole Average" },
  28.     { "strGames", "StrGames", XtRString, sizeof(char *),
  29.       11*sizeof(char *), XtRString, "games" },
  30.     { "personalTitle", "PersonalTitle", XtRString, sizeof(char *),
  31.       12*sizeof(char *), XtRString, "Personal Statistics for %s" }
  32. };
  33.  
  34. void
  35. ScoreRecord::SetDefault(void)
  36. {
  37.     strcpy(name, "                            ");
  38.     hour = 99;
  39.     min  = 99;
  40.     sec  = 99;
  41.     strcpy(date, "00-00-00");
  42.     strcpy(time, "00:00:00");
  43. }
  44.  
  45. void
  46. ScoreRecord::ReadField(FILE *fp, int kanjiCode, int &offset)
  47. {
  48.     char buffer[64];
  49.  
  50.     if (fread((void*)buffer,  sizeof(char), 64, fp) != 64 ||
  51.         buffer[0] == '\0') {
  52.         SetDefault();
  53.         return;
  54.     }
  55.     for(int i=0; i<64; i++) {
  56.         buffer[i] = ((buffer[i] - offset - i) & 0xff);
  57.     }
  58.     offset += 64;
  59.     strncpy(name, buffer, 28);
  60.     name[28] = '\0';
  61.     switch(kanjiCode) {
  62.     case 1:
  63.         strcpy(name, sjis_to_euc(name));
  64.         break;
  65.     case 2:
  66.         strcpy(name, sjis_to_jis(name));
  67.         break;
  68.     }
  69.     hour = atoi(&buffer[35]);
  70.     min  = atoi(&buffer[38]);
  71.     sec  = atoi(&buffer[41]);
  72.     strncpy(date, &buffer[44], 8);
  73.     date[8] = '\0';
  74.     strncpy(time, &buffer[53], 8);
  75.     time[8] = '\0';
  76. }
  77.  
  78. void
  79. ScoreRecord::WriteField(FILE *fp, int kanjiCode, int &cr_offset, int cr)
  80. {
  81.     char buffer[64];
  82.  
  83.     switch(kanjiCode) {
  84.     case 1:
  85.         strcpy(buffer, euc_to_sjis(name));
  86.         break;
  87.     case 2:
  88.         strcpy(buffer, jis_to_sjis(name));
  89.         break;
  90.     default:
  91.         strcpy(buffer, name);
  92.         break;
  93.     }
  94.     buffer[28] = ' ';
  95.     strncpy(&buffer[29], "X" XSHISEN_VERSION, 5);
  96.     buffer[34] = ' ';
  97.     sprintf(&buffer[35], "%2.2d:%2.2d:%2.2d", hour, min, sec);
  98.     buffer[43] = ' ';
  99.     strncpy(&buffer[44], date, 8);
  100.     buffer[52] = ' ';
  101.     strncpy(&buffer[53], time, 8);
  102.     buffer[61] = ' ';
  103.     buffer[62] = '\r';
  104.     buffer[63] = '\n';
  105.     if (cr) {
  106.         for(int i=0; i<64; i++) {
  107.             buffer[i] = ((buffer[i] + cr_offset + i) & 0xff);
  108.         }
  109.         cr_offset += 64;
  110.     }
  111.     fwrite((void*)buffer, sizeof(char), 64, fp);
  112. }
  113.  
  114. void
  115. Score::retry_button(Widget w, XtPointer client_data)
  116. {
  117.     Score *p = (Score *)client_data;
  118. #if USE_MOTIF
  119.     XtUnmanageChild(w);
  120. #else /* USE_MOTIF */
  121.     XtPopdown(p->mdialog);
  122. #endif /* USE_MOTIF */
  123.     p->Register();
  124. }
  125.  
  126. void
  127. Score::abandon_button(Widget w, XtPointer client_data)
  128. {
  129.     Score *p = (Score *)client_data;
  130. #if USE_MOTIF
  131.     XtUnmanageChild(w);
  132. #else /* USE_MOTIF */
  133.     XtPopdown(p->mdialog);
  134. #endif /* USE_MOTIF */
  135.     p->DisplayScore(p->game);
  136. }
  137.  
  138. void
  139. Score::problem(void)
  140. {
  141. #if USE_MOTIF
  142.     if (!mdialog_exist) {
  143.         mdialog = XmCreateWarningDialog(toplevel, "mdialog", NULL, 0);
  144.         XtAddCallback(mdialog, XmNokCallback,
  145.                       (XtCallbackProc)retry_button,
  146.                       (XtPointer)this);
  147.         XtAddCallback(mdialog, XmNcancelCallback,
  148.                       (XtCallbackProc)abandon_button,
  149.                       (XtPointer)this);
  150.         mdialog_exist = 1;
  151.         XtUnmanageChild(XmMessageBoxGetChild(mdialog, XmDIALOG_HELP_BUTTON));
  152.     }
  153.     XtManageChild(mdialog);
  154. #else /* USE_MOTIF */
  155.     if (!mdialog_exist) {
  156.         Widget form, label, b1, b2;
  157.         Position x, y;
  158.         XtVaGetValues(toplevel, XtNx, &x, XtNy, &y, NULL);
  159.         mdialog = XtVaCreatePopupShell("mdialog_popup", transientShellWidgetClass,
  160.                                        toplevel,
  161.                                        XtNx, x+10,
  162.                                        XtNy, y+10,
  163.                                        NULL);
  164.         form = XtVaCreateManagedWidget("mdialog", formWidgetClass, mdialog,
  165.                                        NULL);
  166.         label = XtVaCreateManagedWidget("label", labelWidgetClass, form,
  167.                                         XtNresizable, True,
  168.                                         NULL);
  169.         b1 = XtVaCreateManagedWidget("ok_button", commandWidgetClass, form,
  170.                                      XtNfromVert, label,
  171.                                      NULL);
  172.         b2 = XtVaCreateManagedWidget("cancel_button", commandWidgetClass, form,
  173.                                      XtNfromVert,  label,
  174.                                      XtNfromHoriz, b1,
  175.                                      NULL);
  176.         XtAddCallback(b1, XtNcallback,
  177.                       (XtCallbackProc)retry_button,
  178.                       (XtPointer)this);
  179.         XtAddCallback(b2, XtNcallback,
  180.                       (XtCallbackProc)abandon_button,
  181.                       (XtPointer)this);
  182.         mdialog_exist = 1;
  183.     }
  184.     XtPopup(mdialog, XtGrabNone);
  185. #endif /* USE_MOTIF */
  186. }
  187.  
  188. void
  189. Score::readfile(void)
  190. {
  191.     FILE *fp = fopen(filename, "r");
  192.     int offset;
  193.  
  194.     if (fp == NULL) {
  195.         for(int i=0; i<SCORENUM; i++) {
  196.             rec[i].SetDefault();
  197.         }
  198.         return;
  199.     }
  200.     if (fseek(fp, 1344*game+704, SEEK_SET) != 0)
  201.         fseek(fp, 0, SEEK_END);
  202.     offset = 1;
  203.     for(int i=0; i<SCORENUM; i++){
  204.         rec[i].ReadField(fp, kanjiCode, offset);
  205.     }
  206.     fclose(fp);
  207. }
  208.  
  209. int
  210. Score::writefile(void)
  211. {
  212.     static const char filler[] = "\032"
  213.         "                                                               ";
  214.     FILE *fp = fopen(filename, "r+");
  215.     int offset;
  216.     int i;
  217.  
  218.     if (fp == NULL) {
  219.         problem();
  220.         return 1; // fail!
  221.     }
  222.     offset = 1;
  223.     fseek(fp, 1344*game, SEEK_SET);
  224.     for(i=0; i<SCORENUM; i++) {
  225.         rec[i].WriteField(fp, kanjiCode, offset);
  226.     }
  227.     fwrite((void*)filler, sizeof(filler)-1, 1, fp);
  228.     for(i=0; i<SCORENUM; i++) {
  229.         rec[i].WriteField(fp, kanjiCode, offset, 1);
  230.     }
  231.     fclose(fp);
  232.     return 0; // no problem
  233. }
  234.  
  235. Score::Score(Widget parent)
  236. {
  237. #if USE_MOTIF
  238.     score = XmCreateMessageDialog(parent, "score", NULL, 0);
  239.     XtAddCallback(score, XmNokCallback, (XtCallbackProc)PopDownCB, NULL);
  240.     XtUnmanageChild(XmMessageBoxGetChild(score, XmDIALOG_CANCEL_BUTTON));
  241.     XtUnmanageChild(XmMessageBoxGetChild(score, XmDIALOG_HELP_BUTTON));
  242. #else /* USE_MOTIF */
  243.     Widget form, label, button;
  244.     score = XtVaCreatePopupShell("score", transientShellWidgetClass, parent,
  245.                                  XtNallowShellResize, True,
  246.                                  NULL);
  247.     form = XtVaCreateManagedWidget("mf", formWidgetClass, score,
  248.                                    NULL);
  249.     label = XtVaCreateManagedWidget("label", labelWidgetClass, form,
  250.                                     XtNresizable, True,
  251.                                     XtNtop,       XawChainTop,
  252.                                     XtNbottom,    XawChainBottom,
  253.                                     XtNleft,      XawRubber,
  254.                                     XtNright,     XawRubber,
  255.                                     NULL);
  256.     button = XtVaCreateManagedWidget("ok_button", commandWidgetClass, form,
  257.                                      XtNfromVert, label,
  258.                                      XtNtop,      XawChainTop,
  259.                                      XtNbottom,   XawChainTop,
  260.                                      XtNleft,     XawChainLeft,
  261.                                      XtNright,    XawChainLeft,
  262.                                      XtNlabel,    "OK",
  263.                                      NULL);
  264.     XtAddCallback(button, XtNcallback, (XtCallbackProc)PopDownCB, (XtPointer)score);
  265. #endif /* USE_MOTIF */
  266.     mdialog_exist = 0;
  267.     first_call = 1;
  268. }
  269.  
  270. void
  271. Score::Popup(String str)
  272. {
  273. #if USE_MOTIF
  274.     XmString mstr = XmStringCreateLtoR(str, XmFONTLIST_DEFAULT_TAG);
  275.     XtVaSetValues(score,
  276.                   XmNmessageString, mstr,
  277.                   NULL);
  278.     XmStringFree(mstr);
  279.     XtManageChild(score);
  280. #else /* USE_MOTIF */
  281.     XtVaSetValues(XtNameToWidget(score, "*label"),
  282.                   XtNlabel, str,
  283.                   NULL);
  284.     XtPopup(score, XtGrabNone);
  285. #endif /* USE_MOTIF */
  286. }
  287.  
  288. void
  289. Score::SetScoreFile(const char *scorefile, const char *kcode, const char *personal)
  290. {
  291.     char *home;
  292.  
  293.     filename = strdup(scorefile);
  294.     home = getenv("HOME");
  295.     if (home == NULL)
  296.         home = ".";
  297.     logfile = new char[strlen(home)+strlen(personal)+2];
  298.     sprintf(logfile, "%s/%s", home, personal);
  299.  
  300.     if (strcasecmp(kcode, "euc") == 0)
  301.         kanjiCode = 1;
  302.     else if (strcasecmp(kcode, "jis") == 0)
  303.         kanjiCode = 2;
  304.     else
  305.         kanjiCode = 0;
  306. }
  307.  
  308. void
  309. Score::do_first_call(void)
  310. {
  311.     XtVaGetApplicationResources(score, (XtPointer)res_strings,
  312.                                 resources, XtNumber(resources), NULL);
  313.     first_call = 0;
  314. #if !USE_MOTIF
  315.     Position x, y;
  316.     XtVaGetValues(toplevel, XtNx, &x, XtNy, &y, NULL);
  317.     XtVaSetValues(score, XtNx, x+10, XtNy, y+10, NULL);
  318. #endif /* !USE_MOTIF */
  319. }
  320.  
  321. void
  322. Score::DisplayScore(int kind_of_game)
  323. {
  324.     char format[SCORENUM*80+20], buff[SCORENUM*80];
  325.     long prev_time, this_time;
  326.     int  num;
  327.  
  328.     game = kind_of_game;
  329.     if (first_call) {
  330.         do_first_call();
  331.     }
  332.     // Always read the latest high score
  333.     readfile();
  334.     sprintf(format, res_strings[0]);
  335.     prev_time = -1;
  336.     for(int i=0; i<SCORENUM; i++) {
  337.         this_time = rec[i].hour * 3600 + rec[i].min * 60 + rec[i].sec;
  338.         if (this_time == prev_time)
  339.             num++;
  340.         else
  341.             num = 0;
  342.         prev_time = this_time;
  343.         sprintf(buff, res_strings[1], i+1-num, rec[i].hour, rec[i].min, rec[i].sec,
  344.                 rec[i].name, rec[i].date, rec[i].time);
  345.         strcat(format, buff);
  346.     }
  347.     Popup(format);
  348. }
  349.  
  350. // Convert milisecond data into hour:minute:second
  351. void
  352. Score::ms_to_hms(int ms, unsigned char &h, unsigned char &m, unsigned char &s)
  353. {
  354.     h = ms/3600000;
  355.     m = ms/60000 - h*60;
  356.     s = (ms/1000)%60;
  357. }
  358.  
  359. // Call Score::SetScore formerly!
  360. void
  361. Score::Register(void)
  362. {
  363.     unsigned char m, s, h;
  364.     int    s1, i, mybest;
  365.     int    inspos;
  366.     time_t t;
  367.     struct tm *tp;
  368.     struct passwd *pw;
  369.     char   namebuf[128], myname[NAMELEN+1], gecos[128], *po;
  370.  
  371.     s1 = scoreToRegister / 1000;
  372.     ms_to_hms(scoreToRegister, h, m, s);
  373.     pw = getpwuid(getuid());
  374.     strcpy(gecos, pw->pw_gecos);
  375.     if ((po = strchr(gecos, ',')) != NULL)
  376.         *po = 0;
  377.     sprintf(namebuf, "%-8.8s (%s)", pw->pw_name, gecos);
  378.     sprintf(myname, "%-28.28s", namebuf);
  379.     // Always read the latest high score
  380.     readfile();
  381.  
  382.     // Get my best score in past
  383.     mybest = SCORENUM;
  384. #if !ALLOW_DUPSCORE
  385.     for(i=0; i<SCORENUM; i++) {
  386.         if (strcmp(myname, rec[i].name) == 0) {
  387.             mybest = i;
  388.             break;
  389.         }
  390.     }
  391. #endif /* !ALLOW_DUPSCORE */
  392.  
  393.     // Check if this score values as high score
  394.     inspos = SCORENUM;
  395.     for(i=0; i<SCORENUM; i++) {
  396.         int sx = rec[i].hour * 3600 + rec[i].min * 60 + rec[i].sec;
  397.         if (s1 < sx) {
  398.             inspos = i;
  399.             break;
  400.         }
  401.     }
  402.     if (inspos == SCORENUM || mybest < inspos) {
  403.         return;  // Not high score!
  404.     }
  405.  
  406.     for(i=mybest; i>inspos; i--) {
  407.         if (i>=SCORENUM) continue;
  408.         rec[i] = rec[i-1];
  409.     }
  410.  
  411.     rec[inspos].hour = h;
  412.     rec[inspos].min  = m;
  413.     rec[inspos].sec  = s;
  414.     strcpy(rec[inspos].name, myname);
  415.     time(&t);
  416.     tp = localtime(&t);
  417.     sprintf(rec[i].date, "%2.2d-%2.2d-%2.2d", tp->tm_year, tp->tm_mon+1, tp->tm_mday);
  418.     sprintf(rec[i].time, "%2.2d:%2.2d:%2.2d", tp->tm_hour, tp->tm_min, tp->tm_sec);
  419.     if (writefile() == 0)
  420.         DisplayScore(game);
  421. }
  422.  
  423. void
  424. Score::LogRecord(int flag, int result, int gamesize, int level, int rest)
  425. // flag: 0=giveup 1=finished 2=finish+help 3=tedumari
  426. {
  427.     FILE       *stream;
  428.     time_t     t;
  429.     struct tm  *tp;
  430.     int        check_digit;
  431.  
  432.     stream = fopen(logfile, "a");
  433.     if (stream == NULL)
  434.         return; /* Just do not record, no notifying */
  435.     time(&t);
  436.     tp = localtime(&t);
  437.     check_digit = flag*3 + gamesize + level*7 + tp->tm_year*3 + rest*7
  438.         + tp->tm_mon*7 + tp->tm_mday*3 + tp->tm_hour*7 + tp->tm_min*3 + tp->tm_sec;
  439.     check_digit %= 857;
  440.     fprintf(stream, "%2.2d%2.2d%8.8d%2.2d%1.1d%2.2d%4.4d%2.2d%3.3d%3.3d%2.2d%2.2d\n",
  441.             flag,
  442.             tp->tm_sec, result, tp->tm_min, gamesize, tp->tm_year, level, tp->tm_mon,
  443.             check_digit, rest, tp->tm_mday, tp->tm_hour);
  444.     fclose(stream);
  445. }
  446.  
  447. int
  448. Score::PersonalStat(int kind_of_game)
  449. {
  450.     FILE *s;
  451.     char buf[4096], *bufp, xb[128];
  452.     char scorebuf[SCORENUM*2+1][128];
  453.     int  sec, min, hour, day, month, year;
  454.     int  g, f, c1, c2, l, t;
  455.     int  rest;
  456.     int  w1[2][4], w2[2][4], w3[2][4]; // for average
  457.     int  i;
  458.     struct tm tb;
  459.     unsigned char t1, t2, t3;
  460.     time_t    tm1, tm2;
  461.     struct passwd *pw;
  462.  
  463.     game = kind_of_game;
  464.     time(&tm2);  // Time of now
  465.     if (first_call) {
  466.         do_first_call();
  467.     }
  468.     tm2 -= 86400 * atoi(res_strings[3]);
  469.     s = fopen(logfile, "r");
  470.     if (s == NULL)
  471.         return -1;
  472.     for(i=0; i<SCORENUM*2; i++)
  473.         strcpy(scorebuf[i], "99:99:99 (  0) [00-00-00 00:00:00]");
  474.     memset(w1, 0, 2*4*sizeof(int));
  475.     memset(w2, 0, 2*4*sizeof(int));
  476.     memset(w3, 0, 2*4*sizeof(int));
  477.     while(fgets(buf, 128, s) != NULL) {
  478.         switch(strlen(buf)) {
  479.         case 31:
  480.             // Just for compatibility with xshisen 1.10
  481.             f     = atoiSubstring(buf +  0, 2);
  482.             sec   = atoiSubstring(buf +  2, 2);
  483.             t     = atoiSubstring(buf +  4, 8);
  484.             min   = atoiSubstring(buf + 12, 2);
  485.             g     = atoiSubstring(buf + 14, 1);
  486.             year  = atoiSubstring(buf + 15, 2);
  487.             l     = atoiSubstring(buf + 17, 4);
  488.             month = atoiSubstring(buf + 21, 2);
  489.             c1    = atoiSubstring(buf + 23, 3);
  490.             day   = atoiSubstring(buf + 26, 2);
  491.             hour  = atoiSubstring(buf + 28, 2);
  492.             rest = -1;
  493.             c2 = f*3 + g + l*7 + year*3 + month*7 + day*3 + hour*7 + min*3 + sec;
  494.             break;
  495.         case 34:
  496.             // This is usual format
  497.             f     = atoiSubstring(buf +  0, 2);
  498.             sec   = atoiSubstring(buf +  2, 2);
  499.             t     = atoiSubstring(buf +  4, 8);
  500.             min   = atoiSubstring(buf + 12, 2);
  501.             g     = atoiSubstring(buf + 14, 1);
  502.             year  = atoiSubstring(buf + 15, 2);
  503.             l     = atoiSubstring(buf + 17, 4);
  504.             month = atoiSubstring(buf + 21, 2);
  505.             c1    = atoiSubstring(buf + 23, 3);
  506.             rest  = atoiSubstring(buf + 26, 3);
  507.             day   = atoiSubstring(buf + 29, 2);
  508.             hour  = atoiSubstring(buf + 31, 2);
  509.             c2 = f*3 + g + l*7 + year*3 + month*7 + day*3 + hour*7 + min*3
  510.                 + sec + rest*7;
  511.             break;
  512.         default:
  513.             // Someone edited this file?
  514.             continue;
  515.         }
  516.         c2 %= 857;
  517.         if (c1 != c2 || g != game)
  518.             continue; // Check digit was incorrect, or other game size
  519.         if (f < 0 || f > 3)
  520.             continue; // Invalid value
  521.         l /= 2;
  522.         ms_to_hms(t, t1, t2, t3);
  523.         if (f == 1) {  // Only completed game can be included
  524.             sprintf(scorebuf[SCORENUM*2],
  525.                     "%2.2d:%2.2d:%2.2d (%3d) [%2.2d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d]",
  526.                     t1, t2, t3, l, year, month+1, day, hour, min, sec);
  527.             for(i=SCORENUM*2; i>0 && strcmp(scorebuf[i-1],scorebuf[i])>0; i--) {
  528.                 char p[128];
  529.                 strcpy(p, scorebuf[i-1]);
  530.                 strcpy(scorebuf[i-1], scorebuf[i]);
  531.                 strcpy(scorebuf[i], p);
  532.             }
  533.         }
  534.         tb.tm_isdst = 0;
  535.         tb.tm_sec   = sec;
  536.         tb.tm_min   = min;
  537.         tb.tm_hour  = hour;
  538.         tb.tm_mday  = day;
  539.         tb.tm_mon   = month;
  540.         tb.tm_year  = year;
  541.         tm1 = mktime(&tb);  // Time of the score
  542.         w1[0][f] += t;
  543.         w2[0][f]++;
  544.         w3[0][f] += l;
  545.         if (tm1 >= tm2) { // if the time of the score is newer than specified days ago
  546.             w1[1][f] += t;
  547.             w2[1][f]++;
  548.             w3[1][f] += l;
  549.         }
  550.     }
  551.     fclose(s);
  552.     bufp = buf;
  553.     *bufp = '\0';
  554.     pw = getpwuid(getuid());
  555.     sprintf(bufp, res_strings[12], pw->pw_name);
  556.     bufp += strlen(bufp);
  557.     for(i=0; i<SCORENUM; i++) {
  558.         sprintf(bufp, "%2d ", i+1);
  559.         bufp += 3;
  560.         strcpy(bufp, scorebuf[i]);
  561.         bufp += strlen(bufp);
  562.         sprintf(bufp, "    %2d ", SCORENUM+i+1);
  563.         bufp += 7;
  564.         strcpy(bufp, scorebuf[SCORENUM+i]);
  565.         bufp += strlen(bufp);
  566.         *bufp = '\n';
  567.         bufp++;
  568.     }
  569.     *bufp = '\n'; bufp++;
  570.     *bufp = '\n'; bufp++;
  571.     sprintf(xb, res_strings[2], atoi(res_strings[3]));
  572.     strcpy(bufp, makeStatistics(xb, w1[1], w2[1], w3[1]));
  573.     bufp += strlen(bufp);
  574.     strcpy(bufp, makeStatistics(res_strings[9], w1[0], w2[0], w3[0]));
  575.     bufp += strlen(bufp);
  576.     *bufp = '\0';
  577.     Popup(buf);
  578. }
  579.  
  580. int
  581. Score::atoiSubstring(const char *str, int length)
  582. {
  583.     char b[16];
  584.     strncpy(b, str, length);
  585.     *(b+length) = '\0';
  586.     return atoi(b);
  587. }
  588.  
  589. char
  590. *Score::makeStatistics(const char *title, int rtime[], int rcount[], int rlev[])
  591. {
  592.     int x;
  593.     double y;
  594.     unsigned char t1, t2, t3;
  595.     static char b[1024], *bp;
  596.  
  597.     bp = b;
  598.     x = rcount[0] + rcount[1] + rcount[2] + rcount[3];
  599.     sprintf(bp, "%-20.20s: %6d %s\n", title, x, res_strings[11]);
  600.     bp += strlen(bp);
  601.     if (rcount[1]) {
  602.         x = rtime[1] / rcount[1];
  603.         y = (double)rlev[1] / (double)rcount[1];
  604.         ms_to_hms(x, t1, t2, t3);
  605.         bp += strlen(bp);
  606.         sprintf(bp, "    %-14.14s: %2.2d:%2.2d:%2.2d (%4.1f) %6d %s\n",
  607.                 res_strings[5], t1, t2, t3, y, rcount[1], res_strings[11]);
  608.         bp += strlen(bp);
  609.     }
  610.     if (rcount[3]) {
  611.         x = rtime[3] / rcount[3];
  612.         y = (double)rlev[3] / (double)rcount[3];
  613.         ms_to_hms(x, t1, t2, t3);
  614.         bp += strlen(bp);
  615.         sprintf(bp, "    %-14.14s: %2.2d:%2.2d:%2.2d (%4.1f) %6d %s\n",
  616.                 res_strings[6], t1, t2, t3, y, rcount[3], res_strings[11]);
  617.         bp += strlen(bp);
  618.     }
  619.     if (rcount[2]) {
  620.         x = rtime[2] / rcount[2];
  621.         y = (double)rlev[2] / (double)rcount[2];
  622.         ms_to_hms(x, t1, t2, t3);
  623.         bp += strlen(bp);
  624.         sprintf(bp, "    %-14.14s: %2.2d:%2.2d:%2.2d (%4.1f) %6d %s\n",
  625.                 res_strings[7], t1, t2, t3, y, rcount[2], res_strings[11]);
  626.         bp += strlen(bp);
  627.     }
  628.     if (rcount[0]) {
  629.         x = rtime[0] / rcount[0];
  630.         y = (double)rlev[0] / (double)rcount[0];
  631.         ms_to_hms(x, t1, t2, t3);
  632.         bp += strlen(bp);
  633.         sprintf(bp, "    %-14.14s: %2.2d:%2.2d:%2.2d (%4.1f) %6d %s\n",
  634.                 res_strings[8], t1, t2, t3, y, rcount[0], res_strings[11]);
  635.         bp += strlen(bp);
  636.     }
  637.     *bp = '\0';
  638.     return b;
  639. }
  640.